﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using System.IO;

namespace QA.TFS.Utils
{
    public class BaseXMLDefinition : QA.TFS.Utils.IXMLDefinition
    {

        string _QueryExpression = string.Empty;
        string _NodeExpression = string.Empty;
        XDocument _ConfigXML;
        string _ConfigFile = string.Empty;

        public BaseXMLDefinition()
        {
            this._ConfigFile = "SystemTest.config";
            this._QueryExpression = "//appSettings/add[@key='{0}']";
            this._NodeExpression = "//appSettings/*";
            Initialize();
        }

        public BaseXMLDefinition(string fileName)
        {
            _ConfigFile = fileName;
            this._QueryExpression = "//appSettings/add[@key='{0}']";
            this._NodeExpression = "//appSettings/*";
            Initialize();
        }

        public BaseXMLDefinition(string fileName, string queryExpression)
        {
            this._ConfigFile = fileName;
            this._QueryExpression = queryExpression;
            if (!_QueryExpression.Contains("["))
                this._NodeExpression = this._QueryExpression + "/parent::*/*";
            else
                this._NodeExpression = this._QueryExpression.Substring(0, this._QueryExpression.IndexOf("[")) + "/parent::*/*";
            Initialize();
        }

        public BaseXMLDefinition(string fileName, string queryExpression, string nodeExpression)
        {
            this._ConfigFile = fileName;
            this._QueryExpression = queryExpression;
            this._NodeExpression = nodeExpression;
            Initialize();
        }

        /// <summary>
        /// Inicializa el XDocument
        /// </summary>
        private void Initialize()
        {
            try
            {
                if (!File.Exists(_ConfigFile))
                    throw new ArgumentException("Archivo de configuracion no encontrado. Buscando: " + _ConfigFile);

                _ConfigXML = XDocument.Load(_ConfigFile);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Obtiene en valor de un parametro de configuracion desde el archivo .config
        /// </summary>
        /// <param name="key">Nombre del parametro de configuracion</param>
        /// <returns>Valor del parametro de configuracion</returns>
        public object GetValue(string key)
        {
            try
            {
                XElement _KeyElement = GetElement(key);
                if (_KeyElement != null)
                    return ((XAttribute)_KeyElement.Attribute("value")).Value;
                return null;
            }
            catch (Exception ex)
            {                
                throw ex;
            }
        }

        /// <summary>
        /// Obtiene el registro completo de un parametro de configuracion desde el archivo .config
        /// </summary>
        /// <param name="key">Nombre del parametro de configuracion</param>
        /// <returns>Referencia al completa al elemento de configuracion</returns>
        public virtual XElement GetElement(string key)
        {
            try
            {
                return _ConfigXML.XPathSelectElement(string.Format(this._QueryExpression, key));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Obtiene todos los elementos 
        /// </summary>
        /// <returns>Listado de elementos que correspondan al patron de NodeExpression</returns>
        public virtual List<ConfigItem> GetElements()
        {

            List<ConfigItem> result = new List<ConfigItem>();
            try
            {
                IEnumerable<XElement> elements = _ConfigXML.XPathSelectElements(this._NodeExpression);
                if (elements != null)
                {
                    foreach (XElement item in elements)
                    {
                        string name = item.Attribute("key").Value;
                        string value = item.Attribute("value").Value;

                        result.Add(new ConfigItem(name, value));
                    }
                }
                return result;

            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Aplica los cambios realizados sobre el .config
        /// </summary>
        public void ApplyChanges()
        {
            this.Save(_ConfigFile);
        }

        /// <summary>
        /// Guarda los cambios del XML en una ubicacion especifica
        /// </summary>
        /// <param name="fileName"></param>
        private void Save(string fileName)
        {
            _ConfigXML.Save(fileName);
        }

        /// <summary>
        /// Actualiza el valor de un parametro de configuracion existente
        /// </summary>
        /// <param name="key">Nombre del parametro a actualizar</param>
        /// <param name="value">Nuevo valor del parametro</param>
        public void UpdateKey(string key, string value)
        {
            try
            {
                XElement theElement = GetElement(key);
                if (theElement == null)
                    throw new Exception("XElement nulo");

                ((XAttribute)theElement.Attribute("value")).Value = value;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }
}
